#include "StdAfx.h"
#include "SMBWebview.h"
#include <Imm.h>
#include <functional>
#include "string/strcpcvt.h"
#include "CommonUtil.h"

#pragma comment(lib,"imm32.lib")
#pragma comment(lib,"msimg32.lib")

namespace SOUI {


    //////////////////////////////////////////////////////////////////////////
    // SMBWebView
    
    SMBWebView::SMBWebView(void) : m_pWebView(NULL) {
        GetEventSet()->addEvent(EVENTID(EventMBBrowserTitleChanged));
        GetEventSet()->addEvent(EVENTID(EventMBBrowserNewNav));
        GetEventSet()->addEvent(EVENTID(EventMBBrowserUrlChanged));
        
        m_bFocusable = true;
    }
    
    SMBWebView::~SMBWebView(void) {
    }
    
    //typedef void(MB_CALL_TYPE* mbURLChangedCallback)(mbWebView webView, void* param, const utf8* url, BOOL canGoBack, BOOL canGoForward);
    static void MB_CALL_TYPE _mbURLChangedCallback(mbWebView webView, void *param, const mb_utf8 *url, BOOL canGoBack,
            BOOL canGoForward) {
        ((SMBWebView *)param)->URLChangedCallback(webView, url);
    }
    
    //typedef void(MB_CALL_TYPE* mbPaintUpdatedCallback)(mbWebView webView, void* param, const HDC hdc, int x, int y, int cx, int cy);
    static void MB_CALL_TYPE _mbPaintUpdatedCallback(mbWebView webView, void *param, const HDC hdc, int x, int y, int cx,
            int cy) {
        SMBWebView *_THIS = (SMBWebView *)param;
        
        if (_THIS->IsVisible(TRUE)) {
            CRect rcClient;
            _THIS->GetClientRect(&rcClient);
            CRect rcInvalid(CPoint(x, y), CSize(cx, cy));
            rcInvalid.OffsetRect(rcClient.TopLeft());
            _THIS->InvalidateRect(rcInvalid);
        }
    }
    
    static  void MB_CALL_TYPE _mbLoadUrlEndCallback(mbWebView webView, void *param, const char *url, void *job, void *buf,
            int len) {
        char *html = static_cast<char *>(buf);
        int abcdef = html[0];
    }
    
    static  BOOL MB_CALL_TYPE _mbLoadUrlBeginCallback(mbWebView webView, void *param, const char *url, void *job) {
        SMBWebView *_THIS = (SMBWebView *)param;
        
        if (_THIS->m_hook_url) {
            return _THIS->m_hook_url(url, job);
        } else {
        
        }
        
        return FALSE;
    }
    
    static mbWebView MB_CALL_TYPE _mbCreateViewCallback(mbWebView webView, void *param, mbNavigationType navigationType,
            const mb_utf8 *url, const mbWindowFeatures *windowFeatures) {
        return ((SMBWebView *)param)->CreateViewCallback(webView, navigationType, url, windowFeatures);;
    }
    
    static void MB_CALL_TYPE _mbTitleChangedCallback(mbWebView webView, void *param, const mb_utf8 *title) {
        ((SMBWebView *)param)->TitleChangedCallback(webView, title);
    }
    
    
    static void MB_CALL_TYPE _mbNativeFunction(mbWebView webView, void *param, mbJsExecState es, int64_t queryId,
            int customMsg, const mb_utf8 *request) {
        SMBWebView *_THIS = (SMBWebView *)param;
        if (_THIS->m_hook_js) {
            _THIS->m_hook_js(customMsg, request);
        }
        
        //if (customMsg == 0x123456 && strncmp(request, "loadUrl", 7) == 0)
        //{
        //  SOUI::SStringW sUrl = SOUI::SStrCpCvt::FromUTF8(request);
        //  sUrl = sUrl.Mid(8);
        //  if (!sUrl.IsEmpty())
        //  {
        //      ShellExecute(NULL, _T("open"), sUrl, NULL, NULL, SW_SHOW);
        //  }
        //}
    }
    mbWebView MB_CALL_TYPE handleCreateView(mbWebView webView, void *param, mbNavigationType navigationType,
                                            const mb_utf8 *url, const mbWindowFeatures *windowFeatures) {
        //SOUI::SStringA strUrl = url;
        //RouterDataItem *pDataItem = new RouterDataItem();
        //if (!strUrl.StartsWith("http")) {
        //    int pos = strUrl.Find("://");
        //    if (pos >= 0) {
        //        strUrl = strUrl.Mid(pos + 3);
        //    }
        //}
        //
        //pDataItem->url = SOUI::SStrCpCvt::FromUTF8(strUrl);
        //::PostMessage(theApp.GetXWnd(), WM_X_DO_ROUTER, (WPARAM)pDataItem, 0);
        return 0;
    }
    
    void SMBWebView::BindCallBackFunToWeb() {
        mbOnJsQuery(m_pWebView, _mbNativeFunction, (void *)this);
        //URLChanged
        mbOnURLChanged(m_pWebView, _mbURLChangedCallback, this);
        mbOnTitleChanged(m_pWebView, _mbTitleChangedCallback, this);
        //Updated
        mbOnPaintUpdated(m_pWebView, _mbPaintUpdatedCallback, this);
        mbOnCreateView(m_pWebView, _mbCreateViewCallback, this);
        mbSetNavigationToNewWindowEnable(m_pWebView, TRUE);
        mbSetCspCheckEnable(m_pWebView, FALSE);
        mbSetNpapiPluginsEnabled(m_pWebView, TRUE);
        mbOnLoadUrlBegin(m_pWebView, _mbLoadUrlBeginCallback, this);
        
        mbOnCreateView(m_pWebView, handleCreateView, (void *)this);
    }
    
    void SMBWebView::LoadURL(LPCWSTR url) {
        m_strUrl = url;
        if (m_pWebView) {
            mbLoadURL(m_pWebView, S_CW2A(m_strUrl, CP_UTF8));
        }
    }
    
    SStringW SMBWebView::GetURL() {
        const mb_utf8 *url = mbGetUrl(m_pWebView);
        return S_CA2W(url, CP_UTF8);
    }
    
    void SMBWebView::ReLoad() {
        mbReload(m_pWebView);
    }
    
    bool SMBWebView::DownloadCallback(mbWebView webView, const mb_utf8 *url) {
        return false;
    }
    
    void SMBWebView::URLChangedCallback(mbWebView webView, const mb_utf8 *url) {
        SStringW strUrl = SOUI::SStrCpCvt::FromUTF8(url);
        
        EventMBBrowserUrlChanged evt(this);
        evt.pszUrl = strUrl;
        evt.iId = GetID();
        FireEvent(evt);
    }
    
    void SMBWebView::TitleChangedCallback(mbWebView webView, const mb_utf8 *url) {
        SStringW strUrl = SOUI::SStrCpCvt::FromUTF8(url);
        EventMBBrowserTitleChanged evt(this);
        evt.pszTitle = strUrl;
        evt.iId = GetID();
        FireEvent(evt);
    }
    
    mbWebView SMBWebView::CreateViewCallback(mbWebView webView, mbNavigationType navigationType, const mb_utf8 *url,
            const mbWindowFeatures *windowFeatures) {
        SStringW strUrl = SOUI::SStrCpCvt::FromUTF8(url);
        switch (navigationType) {
            case MB_NAVIGATION_TYPE_LINKCLICK:
            case MB_NAVIGATION_TYPE_FORMSUBMITTE:
            case MB_NAVIGATION_TYPE_BACKFORWARD:
            case MB_NAVIGATION_TYPE_RELOAD:
            case MB_NAVIGATION_TYPE_FORMRESUBMITT:
            case MB_NAVIGATION_TYPE_OTHER:
            default:
            
                break;
        }
        EventMBBrowserNewNav newNav(this);
        newNav.pszUrl = strUrl;
        FireEvent(newNav);
        if (newNav.pRetView) {
            return newNav.pRetView;
        }
        return NULL;
    }
    
    void SMBWebView::StopLoading() {
        mbStopLoading(m_pWebView);
    }
    
    void SMBWebView::GoBack() {
        return mbGoBack(m_pWebView);
    }
    
    void SMBWebView::GoForward() {
        return mbGoForward(m_pWebView);
    }
    
    void MB_CALL_TYPE onRunJs(mbWebView webView, void *param, mbJsExecState es, mbJsValue v) {
    
        SMBWebView *_THIS = (SMBWebView *)param;
        //const mb_utf8 *str = mbJsToString(es, v);
        //if (_THIS->m_hook_js) {
        //    _THIS->m_hook_js(str == nullptr ? -1 : 0, str);
        //}
        auto type = mbGetJsValueType(es, v);
        if (type == kMbJsTypeUndefined || type == kMbJsTypeNull) {
            if (_THIS->m_hook_js) {
                _THIS->m_hook_js(-1, "");
            }
        } else {
            const mb_utf8 *str = mbJsToString(es, v);
            if (_THIS->m_hook_js) {
                _THIS->m_hook_js(0, str);
            }
        }
    }
    
    void SMBWebView::RunJS(const SStringA &strValue) {
    
    
        mbRunJs(m_pWebView, mbWebFrameGetMainFrame(m_pWebView),
                strValue,
                TRUE, onRunJs,
                this,
                nullptr);
                
        //jsValue jsRet = mbRunJS(m_pWebView, S_CW2A(strValue, CP_UTF8));
        //return jsToStringW(wkeGlobalExec(m_pWebView), jsRet);
        //return SStringW();
    }
    
    void SMBWebView::HookUrl(hook_url_call call) {
        m_hook_url = call;
    }
    
    void SMBWebView::HookJS(hook_js_call call) {
        m_hook_js = call;
    }
    
    void SMBWebView::OnPaint(IRenderTarget *pRT) {
        CRect rcClip;
        pRT->GetClipBox(&rcClip);
        CRect rcClient;
        GetClientRect(&rcClient);
        
        CRect rcInvalid;
        rcInvalid.IntersectRect(&rcClip, &rcClient);
        HDC hdc = pRT->GetDC();
        {
            BLENDFUNCTION bf = { AC_SRC_OVER, 0, GetAlpha(), AC_SRC_ALPHA };
            HDC hMbDC = mbGetLockedViewDC(m_pWebView);
            AlphaBlend(hdc, rcInvalid.left, rcInvalid.top, rcInvalid.Width(), rcInvalid.Height(),
                       hMbDC, rcInvalid.left - rcClient.left, rcInvalid.top - rcClient.top, rcInvalid.Width(), rcInvalid.Height(), bf);
            mbUnlockViewDC(m_pWebView);
        }
        
        pRT->ReleaseDC(hdc);
    }
    
    void SMBWebView::OnSize(UINT nType, CSize size) {
        __super::OnSize(nType, size);
        
        if (m_pWebView) {
            CRect rcClient;
            GetClientRect(&rcClient);
            mbSetHandleOffset(m_pWebView, rcClient.left, rcClient.top);
            mbResize(m_pWebView, size.cx, size.cy);
        }
    }
    
    void SMBWebView::OnMove(CPoint pt) {
        SetMsgHandled(FALSE);
    }
    
    void SMBWebView::OnShowWindow(BOOL bShow, UINT nStatus) {
        SetMsgHandled(FALSE);
        //if (m_pWebView) {
        //    mbShowWindow(m_pWebView, bShow);
        //}
    }
    
    int SMBWebView::OnCreate(void *) {
        m_pWebView = mbCreateWebView();
        mbSetHandle(m_pWebView, this->GetContainer()->GetHostHwnd());
        if (!m_pWebView) {
            return 1;
        }
        //wkeSetNavigationToNewWindowEnable((mbWebView)m_pWebView, false);
        BindCallBackFunToWeb();
        if (!m_strUrl.IsEmpty()) {
            mbLoadURL(m_pWebView, S_CW2A(m_strUrl, CP_UTF8));
        }
        return 0;
    }
    
    void SMBWebView::OnDestroy() {
        if (m_pWebView) {
            mbSetHandle(m_pWebView, NULL);
            mbDestroyWebView((mbWebView)m_pWebView);
        }
    }
    LRESULT SMBWebView::OnMouseEvent(UINT message, WPARAM wParam, LPARAM lParam) {
        if (message == WM_LBUTTONDOWN || message == WM_MBUTTONDOWN || message == WM_RBUTTONDOWN) {
            SetFocus();
            SetCapture();
        } else if (message == WM_LBUTTONUP || message == WM_MBUTTONUP || message == WM_RBUTTONUP) {
            ReleaseCapture();
        }
        
        CRect rcClient;
        GetClientRect(&rcClient);
        
        int x = GET_X_LPARAM(lParam) - rcClient.left;
        int y = GET_Y_LPARAM(lParam) - rcClient.top;
        
        unsigned int flags = 0;
        
        if (wParam & MK_CONTROL) {
            flags |= MB_CONTROL;
        }
        if (wParam & MK_SHIFT) {
            flags |= MB_SHIFT;
        }
        
        if (wParam & MK_LBUTTON) {
            flags |= MB_LBUTTON;
        }
        if (wParam & MK_MBUTTON) {
            flags |= MB_MBUTTON;
        }
        if (wParam & MK_RBUTTON) {
            flags |= MB_RBUTTON;
        }
        
        SetMsgHandled(mbFireMouseEvent(m_pWebView, message, x, y, flags));
        return 0;
    }
    
    
    LRESULT SMBWebView::OnKeyDown(UINT uMsg, WPARAM wParam, LPARAM lParam) {
        unsigned int flags = 0;
        if (HIWORD(lParam) & KF_REPEAT) {
            flags |= MB_REPEAT;
        }
        if (HIWORD(lParam) & KF_EXTENDED) {
            flags |= MB_EXTENDED;
        }
        
        SetMsgHandled(mbFireKeyDownEvent(m_pWebView, wParam, flags, false));
        return 0;
    }
    
    LRESULT SMBWebView::OnKeyUp(UINT uMsg, WPARAM wParam, LPARAM lParam) {
        unsigned int flags = 0;
        if (HIWORD(lParam) & KF_REPEAT) {
            flags |= MB_REPEAT;
        }
        if (HIWORD(lParam) & KF_EXTENDED) {
            flags |= MB_EXTENDED;
        }
        
        SetMsgHandled(mbFireKeyUpEvent(m_pWebView, wParam, flags, false));
        return 0;
    }
    
    LRESULT SMBWebView::OnMouseWheel(UINT uMsg, WPARAM wParam, LPARAM lParam) {
        POINT pt;
        pt.x = GET_X_LPARAM(lParam);
        pt.y = GET_Y_LPARAM(lParam);
        
        CRect rc;
        GetWindowRect(&rc);
        pt.x -= rc.left;
        pt.y -= rc.top;
        
        int delta = GET_WHEEL_DELTA_WPARAM(wParam);
        
        unsigned int flags = 0;
        
        if (wParam & MK_CONTROL) {
            flags |= MB_CONTROL;
        }
        if (wParam & MK_SHIFT) {
            flags |= MB_SHIFT;
        }
        
        if (wParam & MK_LBUTTON) {
            flags |= MB_LBUTTON;
        }
        if (wParam & MK_MBUTTON) {
            flags |= MB_MBUTTON;
        }
        if (wParam & MK_RBUTTON) {
            flags |= MB_RBUTTON;
        }
        
        //flags = wParam;
        
        SetMsgHandled(mbFireMouseWheelEvent(m_pWebView, pt.x, pt.y, delta, flags));
        
        return 0;
    }
    
    LRESULT SMBWebView::OnChar(UINT uMsg, WPARAM wParam, LPARAM lParam) {
        unsigned int charCode = wParam;
        unsigned int flags = 0;
        if (HIWORD(lParam) & KF_REPEAT) {
            flags |= MB_REPEAT;
        }
        if (HIWORD(lParam) & KF_EXTENDED) {
            flags |= MB_EXTENDED;
        }
        
        SetMsgHandled(mbFireKeyPressEvent(m_pWebView, charCode, flags, false));
        return 0;
    }
    
    LRESULT SMBWebView::OnImeStartComposition(UINT uMsg, WPARAM wParam, LPARAM lParam) {
        LRESULT result = 0;
        if (mbFireWindowsMessage(m_pWebView, GetContainer()->GetHostHwnd(), WM_IME_STARTCOMPOSITION, 0, 0, &result)) {
            return result;
        }
        
        return 0;
    }
    
    void SMBWebView::OnSetFocus(SWND wndOld) {
        __super::OnSetCursor(wndOld);
        mbSetFocus(m_pWebView);
    }
    
    void SMBWebView::OnKillFocus(SWND wndFocus) {
        mbKillFocus(m_pWebView);
        __super::OnKillFocus(wndFocus);
    }
    
    BOOL SMBWebView::OnSetCursor(const CPoint &pt) {
        LRESULT result = 0;
        if (mbFireWindowsMessage(m_pWebView, GetContainer()->GetHostHwnd(), WM_SETCURSOR, 0, 0, &result)) {
            return result;
        }
        
        return TRUE;
    }
    
    LRESULT SMBWebView::OnAttrUrl(SStringW strValue, BOOL bLoading) {
        m_strUrl = strValue;
        if (!bLoading && m_pWebView) {
            mbLoadURL(m_pWebView, S_CW2A(m_strUrl, CP_UTF8));
        }
        return 0;
    }
}

